package com.varabyte.kobweb.api

import com.varabyte.kobweb.api.stream.ApiStream

/**
 * An annotation which is used to tag either an API method or (optionally) an API stream object.
 *
 * By default, the route of the API method or API stream will be automatically determined from its file path,
 * e.g. "src/api/user/Fetch.kt" -> "/api/user/fetch".
 *
 * Additionally, the name is converted into kebab-case, e.g. "ReserveId.kt" -> "reserve-id"
 *
 * ### API method
 *
 * API methods are suspend functions which are used to serve the result of some API call -- in other words, they
 * represent request / response handshakes. Such a method should take an [ApiContext] as its only parameter.
 *
 * This method must be annotated so that Kobweb can discover and register it with the server:
 *
 * ```
 * @Api
 * suspend fun echo(ctx: ApiContext) { ... }
 * ```
 *
 * ### API stream
 *
 * An API stream represents an open streaming connection over which events can be sent back and forth at any time.
 *
 * If you declare a public, top-level [ApiStream] property, Kobweb can automatically detect and register it with the
 * server. You can tag it with an `@Api` annotation if you'd like, but it's not necessary, unless you additionally want
 * to override its route (which we'll discuss in the next section).
 *
 * ```
 * @Api // Optional in this case
 * val echoStream = object : ApiStream { ... }
 * ```
 *
 * ### routeOverride
 *
 * While the route of the API method or API stream is generated by default, it can be completely customized by setting
 * the annotation's [routeOverride] parameter.
 *
 * In general, you should NOT set it, as this will make it harder for people to navigate your project and find where an
 * API or API stream handler is declared.
 *
 * However, if you do set it, in most cases, it is expected to just be a single, lowercase word, which changes just the
 * final part of the route (replacing what is normally set by the file name).
 *
 * If the value starts with a slash, it will be treated as a full path, useful in case the API method is under some
 * subpackage you don't want to expose to the user for some reason.
 *
 * If the value ends with a slash, it means part of the path will be overridden, but the final part will still be
 * derived from the filename.
 *
 * Some examples should clear up the various cases. Let's say the site is `example.com` and we're using an `@Api`
 * annotation to override the route of an API method defined in `src/api/user/Fetch.kt`:
 *
 * ```
 * // src/api/user/Fetch.kt
 * @Api                     -> example.com/api/user/fetch
 * @Api("retrieve")         -> example.com/api/user/retrieve
 * @Api("/retrieve")        -> example.com/api/retrieve
 * @Api("current/")         -> example.com/api/user/current/fetch
 * @Api("current/retrieve") -> example.com/api/user/current/retrieve
 * @Api("/users/")          -> example.com/api/users/fetch
 * @Api("/users/retrieve")  -> example.com/api/users/retrieve
 * @Api("/")                -> example.com/api/fetch
 * ```
 *
 * ### A note on API methods vs API streams
 *
 * With API methods, the final route represents a URL. With API streams, the final route is just a unique string ID used
 * to identify the stream.
 *
 * As such, if you override the route of an API stream, it will get prepended with "/api/" as part of the URL, while for
 * API streams, it will not:
 *
 * ```
 * // src/api/Example.kt
 *
 * // API method
 * @Api("/a/b/c") // results in the URL path "/api/a/b/c"
 * suspend fun test(ctx: ApiContext) { ... }
 *
 * // API stream
 * @Api("/a/b/c") // results in the ID "a/b/c"
 * val testStream = object : ApiStream { ... }
 * ```
 *
 * Note that the leading slash of the route override in the API stream case is not included in the final ID (although
 * Kobweb would still accept it if you did include it).
 *
 * @param routeOverride If specified, override the logic for generating a route for this API as documented in this
 *   header doc.
 */
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
annotation class Api(val routeOverride: String = "")
